﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using HAP = HtmlAgilityPack;
using System.Text.RegularExpressions;
using StockCommon;
using Utilities;
using StockImport.TaskLog;
namespace StockImport.dataimport.otc.dailyindex
{
    public class OTC_DailyIndexImport
    {
        DateTime valieStartDate = DateTime.Parse("2012/05/29");
        DateTime valieEndDate = DateTime.Parse("2013/05/29");

        public static  void logState(DateTime d)
        {
            DateTime targetDate=d;
            int c=getCount_Otc_OnDate(targetDate);
            if(c>0)
            {
                UpdateScheduleNaviServices.logItem(targetDate, "上櫃_股票行情", c.ToString() + "筆", 2);
            }

            c = getCount_Tse_OnDate(targetDate);
            if (c > 0)
            {
                UpdateScheduleNaviServices.logItem(targetDate, "上市_股票行情", c.ToString() + "筆", 1);
            }






        }
        public bool suitableForDate(DateTime d)
        {
            if ((valieStartDate.CompareTo(d) <= 0) &&
              (valieEndDate.CompareTo(d) >= 0))
                return true;
            return false;
        }
        //--------------------------------------------------------------------
        protected static bool islogComplete(ObjectExecLog exelog)
        {
            if (LogStatus.sUpdated.Equals((LogStatus)exelog.status))
            {
                if (LogResult.rComplete.Equals((LogResult)exelog.result))
                {
                    return true;
                }
            }
            return false;
        }
        //--------------------------------------------------------------------
        protected String getTaskType()
        {
            return "dataimport";
        }
        protected DateTime preCheckDate(DateTime date)
        {
            return date;
        }
        public void updateToDate(DateTime date, int maxHitCount = 200)
        {
            date = DateUtils.getDateOnly(date);
            date = preCheckDate(date);

            Guid id = ObjectExecServices.getServicesId(getTaskType(), getTaskName(), getVersion());
            DateTime targetDate = date;
            int hitCount = 0;

            while (true)
            {
                logState(targetDate);
                if (suitableForDate(targetDate) == false)
                    break;

                if (hitCount > maxHitCount)
                    break;
                ObjectExecLog exelog = ObjectExecServices.getExecLog(id, "imp", targetDate);
                if (islogComplete(exelog))
                {
                    System.Diagnostics.Debug.WriteLine(targetDate.ToShortDateString() + "已經完成,SKIP");
                    hitCount++;

                }
                else
                {
                    bool changed = false;
                    UpdateResult result = update(targetDate, false);
                    exelog.msg = result.getMessage();
                    if (result.getUpdateResult())
                    {
                        exelog.status = (int)LogStatus.sUpdated;
                        if (result.getContentResult())
                        {
                            exelog.result = (int)LogResult.rComplete;
                            changed = true;
                            hitCount++;
                        }
                        else
                        {
                            exelog.result = (int)LogResult.rNoData;
                            changed = true;
                        }
                    }
                    if (changed)
                        ObjectExecServices.updateExecLog(exelog);
                }
                targetDate = targetDate.AddDays(-1);

            }
        }
        /*********************************************************************
         * 
         * 
         * ********************************************************************/
        public string getVersion()
        {
            return "2.0";
        }

        public string getTaskName()
        {
            return "上櫃_股票行情";
        }

        public static int getCount_Tse_OnDate(DateTime d)
        {
            String sql = String.Format("select COUNT(*) from StockDaily where StockDaily.日期 ='{0}' " +
                                      "and StockDaily.證券代號 in (select 代號 from StockIndex where StockIndex.市場='tse')", DateUtils.toYYYYMMDD_Slash(d));
            StockDataClassesDataContext context = LinqAgent.getContext();
            int c = context.ExecuteQuery<int>(sql).FirstOrDefault<int>();
            return c;
        }

        public static int getCount_Otc_OnDate(DateTime d)
        {
            String sql = String.Format("select COUNT(*) from StockDaily where StockDaily.日期 ='{0}' " +
                                      "and StockDaily.證券代號 in (select 代號 from StockIndex where StockIndex.市場='otc')", DateUtils.toYYYYMMDD_Slash(d));
            StockDataClassesDataContext context = LinqAgent.getContext();
            int c = context.ExecuteQuery<int>(sql).FirstOrDefault<int>();
            return c;
        }

        protected static void deleteDataOnDate(DateTime d)
        {
            String sql = "delete from StockDaily where StockDaily.日期 ='" + DateUtils.toYYYYMMDD_Slash(d) +
                "'  and StockDaily.證券代號 in (select 代號 from StockIndex where StockIndex.市場='otc') ";
            StockDataClassesDataContext context = LinqAgent.getContext();
            int c = context.ExecuteCommand(sql);
            System.Diagnostics.Debug.WriteLine("刪除數據:" + c);
            context.SubmitChanges();

        }


        public DateTime resolveRealDate(HAP.HtmlDocument doc)
        {
            HAP.HtmlNodeCollection ns = doc.DocumentNode.SelectNodes("//td[@class='table-title']");
            if (ns == null)
                throw new Exception("No Date Entry");
            HAP.HtmlNode n = ns[0];
            String t = n.InnerText;
            Regex titleRegx = new Regex(@"(\d+)年(\d+)月(\d+)日", RegexOptions.IgnoreCase);
            Match match = titleRegx.Match(t);
            if (match.Success == false)
                throw new Exception("No Date Entry");
            int yy, mm, dd;
            yy = int.Parse(match.Groups[1].Value);
            mm = int.Parse(match.Groups[2].Value);
            dd = int.Parse(match.Groups[3].Value);

            yy = 1911 + yy;
            DateTime realDate = DateTime.Parse(String.Format("{0}/{1}/{2}", yy, mm, dd));
            return realDate;
        }

        public UpdateResult update(DateTime date, bool force = false)
        {
            UpdateResult result = new UpdateResult();
            String fileName = OTC_DailyIndexFetcher.getStoreFileName(date);
            //看content result 跟 update result
            if (date.DayOfWeek.Equals(DayOfWeek.Sunday))
            {
                result.setUpdateResult(true);
                result.setMessage("周日不需更新");
                return result;
            }

            if (System.IO.File.Exists(fileName) == false)
            {
                result.setUpdateResult(true);
                result.setContentResult(false);
                result.setMessage(date.ToShortDateString() + ":" + date.DayOfWeek.ToString() + "\t檔案不存在");
                System.Diagnostics.Debug.WriteLine(result.getMessage());
                return result;
            }

            try
            {
                HAP.HtmlDocument doc = new HAP.HtmlDocument();
                String html = System.IO.File.ReadAllText(fileName, Encoding.GetEncoding("utf-8"));
                doc.LoadHtml(html);
                DateTime realDate = resolveRealDate(doc);
                if (realDate.CompareTo(date) != 0)
                {
                    //沒有這種日期~基本上不會有這種事~
                    return result;
                }
                return update_internal(realDate, doc);
            }
            catch (Exception e)
            {
                result.setMessage(e.Message);
                return result;
            }
        }
        /*********************************************************************************
         * 
         * 
         * ********************************************************************************/

        public enum StatusEnum : int
        {
            Stop = 1,
            Up_Start = 10,
            Up_End = 11,
            Up_Max = 12,
            Up_Min = 13,
            Down_Start = 21,
            Down_End = 22,
            Down_Max = 23,
            Down_Min = 24,
            StockDividen = 51,
            CashDividen = 52
        }
        public class OtcDailyEntry
        {
            public String 代號;
            public String 名稱;
            public Guid id;
            public decimal 收盤;
            public decimal 漲跌;
            public decimal 開盤;
            public decimal 最高;
            public decimal 最低;
            public int 成交股數;
            public decimal 成交金額;
            public int 成交筆數;
            public int 漲跌狀態 = 0; // =1 長 =2 漲停  =-1 跌 -2 跌停  
            public long 發行股數;
            public bool isManaged = false;

            public HashSet<StatusEnum> tagSet = new HashSet<StatusEnum>();

        }

        protected static bool processDecimal(String text, ref decimal value)
        {
            text = text.Trim();
            decimal localdec;
            String[] ss = text.Split(' ');

            if (ss.Length == 1)
            {
                if (decimal.TryParse(ss[0], out localdec))
                {
                    value = localdec;
                    return true;
                }
                return false;
            }
            if (ss[0].Contains("+"))
            {
                if (decimal.TryParse(ss[1], out localdec))
                {
                    value = localdec;
                    return true;
                }
                return false;

            }
            if (ss[0].Contains("-"))
            {
                if (decimal.TryParse(ss[1], out localdec))
                {
                    value = 0 - localdec;
                    return true;
                }
                return false;
            }
            if (decimal.TryParse(ss[1], out localdec))
            {
                value = localdec;
                return true;
            }
            return false;
        }
        /**************************************************************************************
         * 
         * 
         * *************************************************************************************/

        protected void checkLimit(OtcDailyEntry entry, String txt, StatusEnum s1, StatusEnum s2)
        {
            if (txt.Contains("⊕"))
                entry.tagSet.Add(s1);
            if (txt.Contains("⊙"))
                entry.tagSet.Add(s2);
            if (txt.Contains("----"))
                entry.tagSet.Add(StatusEnum.Stop);


        }
        protected void checkDividen(OtcDailyEntry entry, String txt)
        {
            if (txt.Contains("除權"))
                entry.tagSet.Add(StatusEnum.StockDividen);
            if (txt.Contains("除權息"))
                entry.tagSet.Add(StatusEnum.CashDividen);
            if (txt.Contains("除息"))
                entry.tagSet.Add(StatusEnum.CashDividen);
        }
        protected OtcDailyEntry processRow(HAP.HtmlNodeCollection tds)
        {
            OtcDailyEntry entry = new OtcDailyEntry();
            //0 =>代號
            decimal decvalue = 0;

            entry.代號 = tds[0].InnerText.Trim();
            //1名稱	
            entry.名稱 = tds[1].InnerText.Trim();
            //2收盤 	

            if (processDecimal(tds[2].InnerText, ref decvalue))
            {
                entry.收盤 = decvalue;
            }
            checkLimit(entry, tds[2].InnerText, StatusEnum.Up_End, StatusEnum.Down_End);
            //3漲跌	
            if (processDecimal(tds[3].InnerText, ref decvalue))
            {
                entry.漲跌 = decvalue;
            }
            checkDividen(entry, tds[3].InnerText);
            //4開盤 
            if (processDecimal(tds[4].InnerText, ref decvalue))
            {
                entry.開盤 = decvalue;
            }
            checkLimit(entry, tds[4].InnerText, StatusEnum.Up_Start, StatusEnum.Down_Start);
            //5最高 
            if (processDecimal(tds[5].InnerText, ref decvalue))
            {
                entry.最高 = decvalue;
            }
            checkLimit(entry, tds[5].InnerText, StatusEnum.Up_Max, StatusEnum.Down_Max);
            //6最低	
            if (processDecimal(tds[6].InnerText, ref decvalue))
            {
                entry.最低 = decvalue;
            }
            checkLimit(entry, tds[6].InnerText, StatusEnum.Up_Min, StatusEnum.Down_Min);
            //7成交股數 	
            if (processDecimal(tds[7].InnerText, ref decvalue))
            {
                entry.成交股數 = Convert.ToInt32(decvalue);
            }
            //8成交金額(元)	
            if (processDecimal(tds[8].InnerText, ref decvalue))
            {
                entry.成交金額 = decvalue;
            }
            //9	成交筆數
            if (processDecimal(tds[9].InnerText, ref decvalue))
            {
                entry.成交筆數 = Convert.ToInt32(decvalue);
            }
            //10最後買價
            //11最後賣價
            //12發行股數
            if (processDecimal(tds[12].InnerText, ref decvalue))
            {
                entry.發行股數 = Convert.ToInt64(decvalue);
            }
            //13次日漲停價
            //14次日跌停價
            return entry;
        }
        /*********************************************************************************
         * 
         * 
         * ********************************************************************************/
        private UpdateResult update_internal(DateTime realDate, HAP.HtmlDocument doc)
        {
            UpdateResult result = new UpdateResult();
            try
            {

                List<OtcDailyEntry> entries = HtmlToEntries(doc);
                StockManager sm = new StockManager();
                foreach (OtcDailyEntry e in entries)
                {
                    Guid id = sm.getGreTaiId(e.代號);
                    if (Guid.Empty.Equals(id))
                    {
                        id = sm.createGreTai(e.代號, e.名稱, DateTime.Now);
                    }
                    e.id = id;
                }
                //---?>刪除 今天
                deleteDataOnDate(realDate);
                StockDataClassesDataContext context = LinqAgent.getContext();

                foreach (OtcDailyEntry e in entries)
                {
                    StockDaily sd = new StockDaily();
                    sd.日期 = realDate;
                    sd.證券代號 = e.代號;
                    sd.開盤價 = e.開盤;
                    sd.最高價 = e.最高;
                    sd.最低價 = e.最低;
                    sd.收盤價 = e.收盤;
                    sd.成交股數 = e.成交股數;
                    sd.成交金額 = e.成交金額;
                    sd.成交筆數 = e.成交筆數;
                    sd.漲跌價差 = e.漲跌;
                    context.StockDailies.InsertOnSubmit(sd);
                }
                context.SubmitChanges();

                //update 除權息
                StockEventTags stockEventTags = new StockEventTags();
                foreach (OtcDailyEntry e in entries)
                {
                    if (e.tagSet.Count == 0)
                        continue;
                    if (e.tagSet.Contains(StatusEnum.CashDividen))
                        stockEventTags.setCashDividend(e.代號, realDate);
                    if (e.tagSet.Contains(StatusEnum.StockDividen))
                        stockEventTags.setStockDividend(e.代號, realDate);

                    if (e.tagSet.Contains(StatusEnum.Up_Start))
                        stockEventTags.setLimitUP_Start(e.代號, realDate);

                    if (e.tagSet.Contains(StatusEnum.Up_End))
                        stockEventTags.setLimitUP_End(e.代號, realDate);

                    if (e.tagSet.Contains(StatusEnum.Up_Max))
                        stockEventTags.setLimitUP_Max(e.代號, realDate);

                    if (e.tagSet.Contains(StatusEnum.Up_Min))
                        stockEventTags.setLimitUP_Min(e.代號, realDate);

                    if (e.tagSet.Contains(StatusEnum.Down_Start))
                        stockEventTags.setLimitDOWN_Start(e.代號, realDate);

                    if (e.tagSet.Contains(StatusEnum.Down_End))
                        stockEventTags.setLimitDOWN_End(e.代號, realDate);

                    if (e.tagSet.Contains(StatusEnum.Down_Max))
                        stockEventTags.setLimitDOWN_Max(e.代號, realDate);

                    if (e.tagSet.Contains(StatusEnum.Down_Min))
                        stockEventTags.setLimitDOWN_Min(e.代號, realDate);

                }
                stockEventTags.flush();
                result.setUpdateResult(true);
                result.setContentResult(true);
                result.setMessage(realDate.ToShortDateString() + "\t更新資料:" + entries.Count + "筆");
                System.Diagnostics.Debug.WriteLine(result.getMessage());
            }
            catch (Exception e)
            {
                Log.Write(e);
                Log.Write("in OTC_DailyIndexImport.update_internal :" + DateUtils.toYYYYMMDD_Slash(realDate));
            }
            return result;
        }


        private List<OtcDailyEntry> HtmlToEntries(HAP.HtmlDocument doc)
        {
            HAP.HtmlNode table = doc.DocumentNode.SelectSingleNode("//table[@id='contentTable']");
            HAP.HtmlNodeCollection ns = table.SelectNodes(table.XPath + "/tr");
            List<OtcDailyEntry> entries = new List<OtcDailyEntry>();
            foreach (HAP.HtmlNode row in ns)
            {
                HAP.HtmlNodeCollection tds = row.SelectNodes(row.XPath + "/td");
                if (tds.Count != 15)
                    continue;
                OtcDailyEntry entry = processRow(tds);
                entries.Add(entry);
            }
            return entries;
        }
    }
}
